<?php
class Pageley_Util_Reflection {
	private static $classes;
	private static $methods;

	public static function getProperty($subject, $name) {
		$class = self::getReflectionClass($subject);
		$method = self::getGetter($class, $name);
		return $method->invoke($subject);
	}

	public static function setProperty($subject, $name, $value) {
		$class = self::getReflectionClass($subject);
		$method = self::getSetter($class, $name);
		$method->invoke($subject, $value);
	}

	private static function getReflectionClass($object) {
		$className = get_class($object);
		if(self::$classes === null) {
			self::$classes = new Pageley_Util_ArrayMap();
		}
		$class = self::$classes->get($className);
		if($class === null) {
			$class = new ReflectionClass($className);
			self::$classes->put($className, $class);
		}
		return $class;
	}

	private static function getGetter($class, $name) {
		return self::getMethod($class, "get" . Pageley_Util_String::capitalize($name));
	}

	private static function getSetter($class, $name) {
		return self::getMethod($class, "set" . Pageley_Util_String::capitalize($name));
	}

	private static function getMethod($class, $name) {
		$className = $class->getName();
		$methodKey = $className . "::" . $name;
		if(self::$methods === null) {
			self::$methods = new Pageley_Util_ArrayMap();
		}
		$theMethod = self::$methods->get($methodKey);
		if($theMethod === null) {
			$methods = $class->getMethods();
			foreach($methods as $index => $method) {
				$methodName = $method->getName();
				if((Pageley_Util_String::startsWith($methodName, "get") || Pageley_Util_String::startsWith($methodName, "set")) && $method->isPublic()) {
					self::$methods->put($methodKey, $method);
					if($methodName == $name) {
						$theMethod = $method;
						break;
					}
				}
			}
		}
		if($theMethod === null) {
			throw new Pageley_Util_NoSuchMethodException("Method ''" . $class->getName() . "." . $name . "' does not exist.");
		}
		return $theMethod;
	}

	public static function newInstance($className, $constructorArguments = array()) {
		$class = new ReflectionClass($className);
		$instance = null;
		if(count($constructorArguments) > 0) {
			$instance = $class->newInstanceArgs($constructorArguments);
		} else {
			$instance = $class->newInstance();
		}
		return $instance;
	}
    
    public static function invokeMethod($object, $methodName, $arguments = array()) {
		$method = self::getMethod(get_class($object), $methodName);
		return $method->invokeArgs($object, $arguments);
	}

	public static function invokeStaticMethod($className, $methodName, $arguments = array()) {
		$method = new ReflectionMethod($className, $methodName);
		return $method->invokeArgs(null, $arguments);
	}

	public static function getStaticField($className, $fieldName) {
		$class = new ReflectionClass($className);
		return $class->getStaticPropertyValue($fieldName);
	}
}